﻿using System;
using System.Collections.Generic;
using System.Linq;
using Corona.TagContract;
using Corona.TagServer.Exception;
using System.Xml;

namespace Corona.TagServer
{
    public class TagServer : Corona.TagContract.ITagServer
    {
        private CoronaDatabase database=new CoronaDatabase();

        public TagServer()
        {
        }

        public long[] Search(string queryString)
        {
            Query query=null;
            try
            {
                query = new Query(queryString);
               
            }
            catch (CoronaException e)
            {
                Console.WriteLine(e.Message);
            }
            return Search(query);
        }

        /// <summary>
        /// 查询的私有方法，接受的参数类型为Corona.TagServer.Query
        /// </summary>
        /// <param name="query">经过解析的查询</param>
        /// <returns>符合条件的富描述的UID</returns>
        private long[] Search(Query query)
        {
            Dictionary<long, double> UIDScoreMap = new Dictionary<long, double>();
            Dictionary<Tag, Pair<long, long>>.KeyCollection tags = query.TagWeightPair.Keys;

            foreach (Tag tag in tags)
            {
                Pair<long, long> weightPair;
                query.TagWeightPair.TryGetValue(tag, out weightPair);
                IEnumerable<long> UIDs = database.GetUIDsByTag(new QueryTag(tag));
                UpdateScore(UIDs, ref UIDScoreMap, weightPair);
            }

            return SortByScore(UIDScoreMap, query.TargetScore);
        }

        /// <summary>
        /// 用于在一轮查询之后更新得分
        /// </summary>
        /// <param name="UIDs">该轮查询的返回结果</param>
        /// <param name="UIDScoreMap">更新之前的得分情况</param>
        /// <param name="weightPair">本次查询的权重信息</param>
        private void UpdateScore(IEnumerable<long> UIDs, ref Dictionary<long, double> ScoreMap, Pair<long, long> weightPair)
        {
            foreach (long UID in UIDs)
            {
                double score;
                if (ScoreMap.TryGetValue(UID, out score))
                {
                    ScoreMap.Remove(UID);
                    score += weightPair.B;
                    ScoreMap.Add(UID, score);
                }
                else
                {
                    ScoreMap.Add(UID, weightPair.B);
                }
            }
        }

        /// <summary>
        /// 把位于Dictionary中的数据按照分数排序，并返回达到目标分数的结果的UID
        /// </summary>
        /// <param name="UIDScoreMap">数据源</param>
        /// <param name="targetScore">目标分数，达到该分数的结果将被保留</param>
        /// <returns>按照得分排序的富描述的UID</returns>
        private long[] SortByScore(Dictionary<long, double> UIDScoreMap, double targetScore)
        {
            List<long> UIDs = new List<long>();
            var results = from pair in UIDScoreMap orderby pair.Value descending select pair;
            foreach (KeyValuePair<long, double> pair in results)
            {
                if (pair.Value>targetScore-1E-8) UIDs.Add(pair.Key);
            }
            return UIDs.ToArray();
        }

        private void Update(long UID, IEnumerable<Tag> tags)
        {
            foreach (Tag tag in tags)
            {
                database.InsertTagIntoRichDescription(UID, tag);
            }
        }


        /// <summary>
        /// 未验证通过返回0，发生不能异常返回-1，正常执行返回富描述的UID
        /// </summary>
        /// <param name="queryString"></param>
        /// <param name="appVerify"></param>
        /// <returns></returns>
        public long GetStrongLink(string queryString, string appVerify)
        {
            long UID=-1;
            if (VerifyApplication(appVerify))
            {
                try
                {
                    Query query = new Query(queryString);
                    long[] UIDs = Search(query);

                    if (UIDs.Length == 0)
                    {
                        UID = database.GetNextValidUID();
                        Update(UID, query.TagWeightPair.Keys);
                    }
                    else
                    {
                        UID=UIDs[0];
                    }
                }
                catch (CoronaException e)
                {
                    Console.WriteLine(e.Message);
                }
            }
            else
            {
                UID= 0;
            }
            return UID;
        }

        public string GetXmlByUID(long UID,string[] tagNames)
        {
            string xml = null;
            try
            {
                xml=database.GetRichDescriptionXmlDocument(UID, tagNames).OuterXml;
            }
            catch (CoronaException e)
            {
                Console.WriteLine(e.Message);
            }
            return xml;
        }

        public int Update(long UID, IEnumerable<Tag> tags, string appVerify)
        {
            if (VerifyApplication(appVerify))
            {
                try
                {
                    Update(UID, tags);
                    return 1;
                }
                catch (CoronaException e)
                {
                    Console.WriteLine(e.Message);
                    return -1;
                }
            }
            else
                return 0;
        }

        public int RemoveTag(long uid, Tag tag, string appVerify)
        {
            if (VerifyApplication(appVerify))
            {
                return -1;
            }
            else
                return 0;
        }

        public TagJar GetTagJar(TagJar tagjar,double relevance)
        {
            if (tagjar == null)
                return null;
            Pair<Tag, long>[] relations = tagjar.Relations;
            Dictionary<Tag, long> ScoreMap = new Dictionary<Tag, long>();
            foreach (Pair<Tag, long> relation in relations)
            {
                IEnumerable<Pair<Tag,long>> relatedTags= database.GetRelatedTags(relation.A);
                foreach (Pair<Tag, long> relatedTag in relatedTags)
                {
                    long score = 0;

                    if (ScoreMap.TryGetValue(relatedTag.A, out score))
                    {
                        ScoreMap.Remove(relatedTag.A);
                        score+=relatedTag.B*relation.B;
                        ScoreMap.Add(relatedTag.A, score);
                    }
                    else
                    {
                        ScoreMap.Add(relatedTag.A, relatedTag.B * relation.B);
                    }
                }
            }

            List<Pair<Tag, long>> result = new List<Pair<Tag, long>>();
            double targetScore=0;
            var results = from pair in ScoreMap orderby pair.Value descending select pair;
            foreach (KeyValuePair<Tag,long> pair in results)
            {
                targetScore = pair.Value * relevance;
                break;
            }
            foreach (KeyValuePair<Tag, long> pair in results)
            {
                if (pair.Value > targetScore - 1E-8) result.Add(new Pair<Tag, long>(pair.Key, pair.Value));
            }
            TagJar newTagJar = new TagJar();
            newTagJar.Relations=result.ToArray();
            return newTagJar;
        }

        public string RegisterApp(string registerXml)
        {
            XmlDocument xml = new XmlDocument();
            xml.LoadXml(registerXml);


            XmlNamespaceManager nsmgr = new XmlNamespaceManager(xml.NameTable);
            nsmgr.AddNamespace("corona", "http://www.CoronaTagAnyWhere.com");


            XmlNode appRegister = xml.SelectSingleNode("/corona:AppRegister", nsmgr);
            if (appRegister == null)
            {
                Console.WriteLine("Error in Reading xml of client");
                return null;
            }

            string appName = appRegister.Attributes["Name"].Value;
            long authority = 5;
            try
            {
                database.RegisterApplication(registerXml, appName, authority);
            }
            catch (DatabaseException e)
            {
                Console.WriteLine(e.Message);
            }
            return registerXml;
        }

        /// <summary>
        /// 验证应用程序的正确性及权限
        /// </summary>
        /// <param name="appVerify">应用程序的验证字符串</param>
        /// <returns>应用程序是否通过验证</returns>
        private bool VerifyApplication(string appVerify)
        {
            string[] temp = appVerify.Split(':');
            string appName = temp[0];
            string MD5 = temp[1];
            return MD5.Equals(database.GetAppVerifyInfo(appName));
        }
    }
}